<?php

namespace App\Ma\Weights;

use App\Lib\_Error\ExecuteException;
use App\Ma\Entities\Matrix;
use App\Ma\Entities\MatrixUtils;

class BaseWeight
{
    /**
     * @var Matrix
     */
    public $matrix;

    /**
     * 整付手牌权重
     * @var int
     */
    public $weight = 0;

    /**
     * 牌型总数
     */
    const TYPE_COUNT_KEY = -1;

    /**
     * 牌型总权值占比
     */
    const TYPE_WEIGHT_KEY = -2;

    /**
     * 手牌权值矩阵
     * @var array
     */
    public $weightMatrix;

    /**
     * 基础权值+10
     */
    const DEFAULT_WEIGHT = 10;

    /**
     * 相隔+10
     */
    const XIANG_GE = 10;

    /**
     * 相邻+20
     */
    const XIANG_LIN = 20;

    /**
     * 2张相同+20
     */
    const XIANG_TONG_2 = 20;

    /**
     * 3张相同+30
     */
    const XIANG_TONG_3 = 30;


    /**
     * 4张相同+40
     */
    const XIANG_TONG_4 = 40;

    public function __construct(Matrix $matrix)
    {
        $this->matrix = $matrix;

        $this->initWeightMatrix();
        $this->fillWeightMatrix();
    }

    /**
     * 判断是否需要杠（机器人使用）
     * @param int $value
     * @return BaseWeight|false
     */
    public function wantGang(int $value)
    {
        $matrix = clone $this->matrix;
        if ($matrix->gang($value)) {
            $weight = new static($matrix);
            // 比较杠后的总权值大小
            if ($weight->weight > $this->weight) {
                return $weight;
            }
        }
        return false;

    }

    /**
     * 判断是否需要碰（机器人使用）
     * @param int $value
     * @return BaseWeight|false
     */
    public function wantPeng(int $value)
    {
        $matrix = clone $this->matrix;
        if ($matrix->peng($value)) {
            $weight = new static($matrix);
            // 比较碰后的总权值大小
            if ($weight->weight > $this->weight) {
                return $weight;
            }
        }
        return false;
    }

    /**
     * 获取最小权重的牌
     * @return mixed
     * @throws ExecuteException
     */
    public function getMinWeightCard()
    {
        $weightMatrix = $this->matrix;
        $minCards = [];
        $minWeight = 0;
        foreach ($weightMatrix as $type => $list) {
            foreach ($list as $key => $weight) {
                if ($weight <= 0) {
                    continue;
                }
                if ($weight < $minWeight || empty($minCards)) {
                    $minWeight = $weight;
                    array_push($minCards, MatrixUtils::generateHexValue($type, $key + 1));
                }
            }

            if ($type >= Matrix::TYPE_SAN) {
                // 后续的鬼牌不出
                break;
            }
        }
        if (empty($minCards)) {
            throw new ExecuteException('手牌异常');
        }
        return $minCards[0];
    }

    /**
     * 1、初始化权值矩阵
     */
    protected function initWeightMatrix()
    {
        $matrix = Matrix::Matrix;
        $matrix[Matrix::TYPE_WAN][self::TYPE_COUNT_KEY] = 0;
        $matrix[Matrix::TYPE_WAN][self::TYPE_WEIGHT_KEY] = 0;

        $matrix[Matrix::TYPE_TONG][self::TYPE_COUNT_KEY] = 0;
        $matrix[Matrix::TYPE_TONG][self::TYPE_WEIGHT_KEY] = 0;

        $matrix[Matrix::TYPE_TIAO][self::TYPE_COUNT_KEY] = 0;
        $matrix[Matrix::TYPE_TIAO][self::TYPE_WEIGHT_KEY] = 0;

        $matrix[Matrix::TYPE_ZI][self::TYPE_COUNT_KEY] = 0;
        $matrix[Matrix::TYPE_ZI][self::TYPE_WEIGHT_KEY] = 0;

        $matrix[Matrix::TYPE_SAN][self::TYPE_COUNT_KEY] = 0;
        $matrix[Matrix::TYPE_SAN][self::TYPE_WEIGHT_KEY] = 0;
    }

    /**
     * 2、计算权值矩阵
     */
    protected function fillWeightMatrix()
    {

        $cardsLenght = count($this->matrix->cards);
        /**
         * 用户手牌矩阵
         */
        $matrix = $this->matrix->cardMatrix;

        /**
         * 手牌权值矩阵
         */
        $weightMatrix = $this->weightMatrix;

        foreach ($matrix as $type => $list) {
            // 每种牌型的总数量
            $typeCount = 0;
            // 牌型在手牌中的占比
            $typeWeight = 0;
            foreach ($matrix[$type] as $key => $count) {
                if ($count <= 0) {
                    continue;
                }
                $weight = self::DEFAULT_WEIGHT;
                $typeCount += $count;
                // 相同
                switch ($count) {
                    case 2:
                        $weight += $count * self::XIANG_TONG_2;
                        break;
                    case 3:
                        $weight += $count * self::XIANG_TONG_3;
                        break;
                    case 4:
                        $weight += $count * self::XIANG_TONG_4;
                        break;
                }
                if ($type < Matrix::TYPE_ZI) { // 仅花牌有相邻、相隔权值

                    // 相邻
                    if ($matrix[$type][$key - 1] ?? 0) {
                        $weight += self::XIANG_LIN;
                    }
                    if ($matrix[$type][$key + 1] ?? 0) {
                        $weight += self::XIANG_LIN;
                    }

                    // 相隔
                    if ($matrix[$type][$key - 2] ?? 0) {
                        $weight += self::XIANG_LIN;
                    }
                    if ($matrix[$type][$key + 2] ?? 0) {
                        $weight += self::XIANG_LIN;
                    }
                }
                $weightMatrix[$type][$key] = $weight;
                $typeWeight += $weight;

            }
            $this->weight += $typeWeight;
            // 权值在手牌中的占比
            $typeWeight = \bcmul($typeWeight, 1 + $typeCount / $cardsLenght);
            $weightMatrix[$type][self::TYPE_COUNT_KEY] = $typeCount;
            $weightMatrix[$type][self::TYPE_WEIGHT_KEY] = $typeWeight;
            if ($type >= Matrix::TYPE_SAN) {
                // 后续的鬼牌不计算权值
                break;
            }
        }
        $this->weightMatrix = $weightMatrix;
    }
}